import asyncio
import io
from functools import lru_cache
from typing import BinaryIO, Optional

from minio import Minio
from minio.error import S3Error

from app.core.config import settings
from app.core.logging_config import get_logger

logger = get_logger(__name__)


class MinIOClient:
    def __init__(self):
        self.client = Minio(
            endpoint=settings.MINIO_ENDPOINT.replace("https://", "").replace("http://", ""),
            access_key=settings.MINIO_ACCESS_KEY,
            secret_key=settings.MINIO_SECRET_KEY,
            secure=settings.MINIO_SECURE,
            region=settings.MINIO_REGION
        )
        self.bucket_name = settings.MINIO_BUCKET_NAME
        self._ensure_bucket()

    def _ensure_bucket(self):
        """确保存储桶存在，如果不存在则创建"""
        try:
            if not self.client.bucket_exists(self.bucket_name):
                self.client.make_bucket(self.bucket_name, location=settings.MINIO_REGION)
        except S3Error as e:
            logger.error(f"Error creating bucket: {e}")

    async def upload_file(self, object_name: str, file_data: BinaryIO, content_type: str = None, file_size: int = None, directory: str = None) -> str:
        """
        异步上传文件到MinIO
        
        Args:
            object_name: 对象名称/路径
            file_data: 文件数据
            content_type: 文件内容类型
            file_size: 文件大小
            directory: 目录路径（可选）
            
        Returns:
            full_object_name: 上传成功的完整对象名称（包含目录路径）
        """
        try:
            # 构建完整的对象路径
            if directory:
                # 确保目录路径以 / 结尾，且不以 / 开头
                directory = directory.strip('/')
                if directory:
                    full_object_name = f"{directory}/{object_name}"
                else:
                    full_object_name = object_name
            else:
                # 使用默认目录
                default_dir = settings.MINIO_UPLOAD_DIR.strip('/')
                if default_dir:
                    full_object_name = f"{default_dir}/{object_name}"
                else:
                    full_object_name = object_name
            
            # 由于minio-py不支持async，在线程池中运行
            loop = asyncio.get_event_loop()
            
            await loop.run_in_executor(
                None,
                self.client.put_object,
                self.bucket_name,
                full_object_name,
                file_data,
                file_size,
                content_type
            )
            
            return full_object_name
        except S3Error as e:
            raise Exception(f"Failed to upload file to MinIO: {e}")

    async def download_file(self, object_name: str) -> bytes:
        """
        异步从MinIO下载文件
        
        Args:
            object_name: 对象名称/路径
            
        Returns:
            文件内容字节
        """
        try:
            loop = asyncio.get_event_loop()
            
            response = await loop.run_in_executor(
                None,
                self.client.get_object,
                self.bucket_name,
                object_name
            )
            
            data = response.read()
            response.close()
            response.release_conn()
            
            return data
        except S3Error as e:
            raise Exception(f"Failed to download file from MinIO: {e}")

    async def delete_file(self, object_name: str) -> bool:
        """
        异步删除MinIO中的文件
        
        Args:
            object_name: 对象名称/路径
            
        Returns:
            删除成功返回True
        """
        try:
            loop = asyncio.get_event_loop()
            
            await loop.run_in_executor(
                None,
                self.client.remove_object,
                self.bucket_name,
                object_name
            )
            
            return True
        except S3Error as e:
            logger.error(f"Failed to delete file from MinIO: {e}")
            return False

    async def get_file_url(self, object_name: str, expires: int = 3600) -> str:
        """
        获取文件的预签名URL
        
        Args:
            object_name: 对象名称/路径
            expires: URL过期时间（秒），默认1小时
            
        Returns:
            预签名URL
        """
        try:
            loop = asyncio.get_event_loop()
            
            url = await loop.run_in_executor(
                None,
                self.client.presigned_get_object,
                self.bucket_name,
                object_name,
                expires
            )
            
            return url
        except S3Error as e:
            raise Exception(f"Failed to generate presigned URL: {e}")

    async def file_exists(self, object_name: str) -> bool:
        """
        检查文件是否存在
        
        Args:
            object_name: 对象名称/路径
            
        Returns:
            文件存在返回True
        """
        try:
            loop = asyncio.get_event_loop()
            
            await loop.run_in_executor(
                None,
                self.client.stat_object,
                self.bucket_name,
                object_name
            )
            
            return True
        except S3Error:
            return False


@lru_cache()
def get_minio_client() -> MinIOClient:
    """获取MinIO客户端实例（单例模式）"""
    return MinIOClient()